#include "../config.h"

#include <glibmm/i18n.h>

#include "application.h"
#include "command.h"
#include "log.h"
#include "favorite.h"
#include "yellowpage.h"

namespace gpyp {

//========================
// Rule
void Rule::update_re() {
  re_.reset();
  re_valid_ = true;
  if (!(*word_).empty()) {
    try {
      re_ = Regex::create(*word_, !*case_sensitive_);
    } catch(...) {
      re_valid_ = false;
    }
  }
}

void Rule::set_target(Target target) {
  target_name_    = (target & TARGET_NAME)    != 0;
  target_genre_   = (target & TARGET_GENRE)   != 0;
  target_desc_    = (target & TARGET_DESC)    != 0;
  target_comment_ = (target & TARGET_COMMENT) != 0;
  target_track_   = (target & TARGET_TRACK)   != 0;
  target_contact_ = (target & TARGET_CONTACT) != 0;
  target_ypname_  = (target & TARGET_YPNAME)  != 0;
  target_type_    = (target & TARGET_TYPE)    != 0;
  target_id_      = (target & TARGET_ID)      != 0;
  target_tip_     = (target & TARGET_TIP)     != 0;
}

bool Rule::match(Channel &ch) {
  if ((*word_).empty()) return *invert_;
  if (!re_) return false;

  bool match = ((*target_name_ &&
                 re_->match(ch.get_name())) ||
                (*target_genre_ &&
                 re_->match(ch.get_genre())) ||
                (*target_desc_ &&
                 re_->match(ch.get_desc())) ||
                (*target_comment_ &&
                 re_->match(ch.get_comment())) ||
                (*target_track_ &&
                 (re_->match(ch.get_track_artist()) ||
                  re_->match(ch.get_track_album()) ||
                  re_->match(ch.get_track_title()))) ||
                (*target_contact_ &&
                 re_->match(ch.get_contact())) ||
                (*target_ypname_ &&
                 re_->match(ch.get_ypname())) ||
                (*target_type_ &&
                 re_->match(ch.get_type())) ||
                (*target_id_ &&
                 re_->match(ch.get_id_str())) ||
                (*target_tip_ &&
                 re_->match(ch.get_tip())));
  return *invert_ ? !match : match;
}

Rule::Rule(const Glib::ustring &name) :
  conf::Element  (name),
  word_          ("word",            ""),
  case_sensitive_("case_sensitive",  false),
  invert_        ("invert",          false),
  target_name_   ("target_name",     false),
  target_genre_  ("target_genre",    false),
  target_desc_   ("target_desc",     false),
  target_comment_("target_comment",  false),
  target_track_  ("target_track",    false),
  target_contact_("target_contact",  false),
  target_ypname_ ("target_ypname",   false),
  target_type_   ("target_type",     false),
  target_id_     ("target_id",       false),
  target_tip_    ("target_tip",      false),
  re_valid_      (true)
{
  word_->signal_changed().connect(sigc::mem_fun(*this, &Rule::update_re));
  case_sensitive_->signal_changed()
    .connect(sigc::mem_fun(*this, &Rule::update_re));

  add(word_);
  add(case_sensitive_);
  add(invert_);
  add(target_name_);
  add(target_genre_);
  add(target_desc_);
  add(target_comment_);
  add(target_track_);
  add(target_contact_);
  add(target_ypname_);
  add(target_type_);
  add(target_id_);
  add(target_tip_);
}

//========================
// AttributeChSort
AttributeChSort::NameTable AttributeChSort::_name_table[] = {
  {CHSORT_NONE,    "none"},
  {CHSORT_TOP,     "top"},
  {CHSORT_MIDDLE,  "middle"},
  {CHSORT_BOTTOM,  "bottom"},
  {CHSORT_INVALID, NULL}
};

//========================
// Favorite
RefRule Favorite::on_create_item(const Glib::ustring &name) {
  return RefRule(new Rule(name));
}

RefRule Favorite::add_item() {
  return conf::Collection<Rule, Favorite>::add_item();
}
RefRule Favorite::add_item(const Glib::ustring &word,
                           Rule::Target target,
                           bool case_sensitive,
                           bool invert) {
  RefRule rule           = add_item();
  rule->word()           = word;
  rule->case_sensitive() = case_sensitive;
  rule->invert()         = invert;
  rule->set_target(target);
  return rule;
}
RefRule Favorite::add_item(Rule::Target target) {
  RefRule rule = add_item();
  rule->set_target(target);
  return rule;
}

bool Favorite::match(Channel &ch) {
  if (*rule_age_min_ >= 0) {
    if (ch.get_age() >= 0) {
      if (ch.get_age() < *rule_age_min_) return false;
    } else {
      return false;
    }
  }
  if (*rule_age_max_ >= 0) {
    if (ch.get_age() >= 0) {
      if (ch.get_age() > *rule_age_max_) return false;
    } else {
      return false;
    }
  }
  if (begin() == end()) {
    return true;
  }
  for(iterator iter = begin(); iter != end(); ++iter) {
    if ((*iter)->match(ch) == *rule_or_) {
      return *rule_or_;
    }
  }
  return !*rule_or_;
}

Favorite::Favorite(const Glib::ustring &name) :
  conf::Collection<Rule, Favorite>(name, "rule"),
  name_               ("name",                "Favorite"),
  enable_             ("enable",              true),
  filter_             ("filter",              false),
  rule_or_            ("rule_or",             false),
  rule_age_min_       ("rule_age_min",        -1),
  rule_age_max_       ("rule_age_max",        -1),

  bgcolor_            ("bgcolor",             Color()),
  fgcolor_            ("fgcolor",             Color()),
  font_               ("font",                ""),
  sort_               ("sort",                CHSORT_NONE),
  ng_                 ("ng",                  boost::logic::indeterminate),
  auto_play_          ("auto_play",           boost::logic::indeterminate),
  auto_record_        ("auto_record",         boost::logic::indeterminate),
  notify_new_         ("notify_new",          boost::logic::indeterminate),
  notify_change_      ("notify_change",       boost::logic::indeterminate),
  notify_sound_       ("notify_sound",        ""),
  notify_sound_volume_("notify_sound_volume", 1)
{
  add(name_);
  add(enable_);
  add(filter_);
  add(rule_or_);
  add(rule_age_min_);
  add(rule_age_max_);

  add(bgcolor_);
  add(fgcolor_);
  add(font_);
  add(sort_);
  add(ng_);
  add(auto_play_);
  add(auto_record_);
  add(notify_new_);
  add(notify_change_);
  add(notify_sound_);
  add(notify_sound_volume_);
}

//========================
// FavoriteCollection
const char *FavoriteCollection::PRESET_BANDWIDTH        = "\\(要帯域チェック|\\(再生不可|\\(要ポート開放\\)|\\(切断\\)";

const char *FavoriteCollection::PRESET_YPINFO_GENERAL   = "^[A-Z\\d]+◆";
const char *FavoriteCollection::PRESET_YPINFO_INDEX_TXT = "^index(?:_[a-z]+)?\\.txt$";
const char *FavoriteCollection::PRESET_YPINFO_MISC      = "^(?:[LN]Pinfo|YPミラー◆|OekakiYP_INFO|Temporary yellow Pages◆|TPからのお知らせ◆|TwitterYP)";

RefFavorite FavoriteCollection::on_create_item(const Glib::ustring &name) {
  return RefFavorite(new Favorite(name));
}
void FavoriteCollection::on_load_default() {
  conf::Collection<Favorite, FavoriteCollection>::on_load_default();
  add_item("CP",   true,  true, Color("#e2f5e2"))->add_item("^CP$",   Rule::TARGET_YPNAME, true);
  add_item("XP",   true,  true, Color("#e2e9f5"))->add_item("^XP$",   Rule::TARGET_YPNAME, true);
  add_item("TP",   true,  true, Color("#f5f5e2"))->add_item("^TP$",   Rule::TARGET_YPNAME, true);
  add_item("RP",   true,  true, Color("#f5e2f5"))->add_item("^RP$",   Rule::TARGET_YPNAME, true);
  add_item("SP",   true,  true, Color("#f5e2e2"))->add_item("^SP$",   Rule::TARGET_YPNAME, true);
  add_item("YPM",  true,  true)                  ->add_item("^YPM$",  Rule::TARGET_YPNAME, true);
  add_item("YP",   false, true)                  ->add_item("^YP$",   Rule::TARGET_YPNAME, true);

  add_item(_("Bandwidth check required/Port closed"), true, false, Color(), Color("#a40000"))
    ->add_item(PRESET_BANDWIDTH, Rule::TARGET_NAME);
  RefFavorite f_ypinf(add_item(_("YP information"), true, false, Color(), Color("#888a85"), CHSORT_BOTTOM));
  f_ypinf->add_item(PRESET_YPINFO_GENERAL,   Rule::TARGET_NAME, true);
  f_ypinf->add_item(PRESET_YPINFO_INDEX_TXT, Rule::TARGET_NAME);
  f_ypinf->add_item(PRESET_YPINFO_MISC,      Rule::TARGET_NAME, true);
  f_ypinf->rule_or() = true;
  RefFavorite f_xpent(add_item(_("XP Entry"), true, false, Color(), Color("#a40000"), CHSORT_TOP));
  f_xpent->add_item("^XP$", Rule::TARGET_YPNAME, true);
  f_xpent->add_item("^Entry$", Rule::TARGET_GENRE);
  f_xpent->add_item("Stream ch is down|Normal YP mode", Rule::TARGET_COMMENT, false, true);
  RefFavorite f_new(add_item(_("New")));
  f_new->font() = "Bold";
  f_new->add_item("^[0-9A-F]{32}$", Rule::TARGET_ID);
  f_new->add_item("^0*$", Rule::TARGET_ID, false, true);
  f_new->rule_age_max() = 10;

  add_item(_("Favorite"), true, true, Color("#add5ff"))
    ->add_item(Rule::TARGET_NAME);
  RefFavorite ng = add_item(_("NG"), true, true);
  ng->ng() = true;
  ng->add_item(Rule::TARGET_NAME);
}

RefFavorite FavoriteCollection::add_item() {
  return conf::Collection<Favorite, FavoriteCollection>::add_item();
}
RefFavorite FavoriteCollection::add_item(const Glib::ustring &name,
                                         bool                 enable,
                                         bool                 filter,
                                         const Color         &bgcolor,
                                         const Color         &fgcolor,
                                         ChSort               sort) {
  RefFavorite favorite = add_item();
  favorite->name()     = name;
  favorite->enable()   = enable;
  favorite->filter()   = filter;
  favorite->bgcolor()  = bgcolor;
  favorite->fgcolor()  = fgcolor;
  favorite->sort()     = sort;
  return favorite;
}

RefFavorite FavoriteCollection::find(const Glib::ustring &name) {
  for(iterator iter = begin(); iter != end(); ++iter) {
    if (*(*iter)->name() == name) return *iter;
  }
  return RefFavorite();
}
RefFavorite FavoriteCollection::find(RefRegex re) {
  for(iterator iter = begin(); iter != end(); ++iter) {
    if (re->match(*(*iter)->name())) return *iter;
  }
  return RefFavorite();
}

bool FavoriteCollection::bc_rule_append_word(ShellLine   &line,
                                             CommandOpts &opts) {
  if (!opts.rule && (!opts.favorite || opts.favorite->size() < 1)) {
    log::err << "rule or favorite not specified or favorite has no rule"
             << log::endl;
    return false;
  }

  OptionParser  parser;
  bool s = false, n = false;
  parser.add("-s", s);
  parser.add("-n", n);

  if (parser.parse(line)) {
    if (line.args.size() != 1) {
      log::err << "invalid arguments" << log::endl;
      return false;
    }

    RefRule rule = opts.rule ? opts.rule : *opts.favorite->begin();
    Glib::ustring word;
    if (!n && !(*rule->word()).empty()) word = "|";
    if (s) word += "^";
    word += Regex::escape(line.args.front());
    if (s) word += "$";
    rule->word() = *rule->word() + word;
    return true;
  } else {
    return false;
  }
}

FavoriteCollection::FavoriteCollection(Application &app) :
  conf::Collection<Favorite, FavoriteCollection>("favorites", "favorite"),
  app_        (app),
  bc_favorite_(*app_.bc().add_ns("Favorite")),
  bc_rule_    (*bc_favorite_.add_ns("Rule"))
{
#define BC_ADD_CMD(NS, CMD, H)                                          \
  bc_##NS##_.add_cmd(#CMD, sigc::mem_fun(*this, &FavoriteCollection::bc_##NS##_##CMD), H)

  BC_ADD_CMD(rule, append_word, _("Append word to rule"));
}

//========================
// FavoriteMatch
FavoriteMatch::FavoriteMatch(RefFavoriteCollection favorites, Channel &ch) :
  sort_             (CHSORT_MIDDLE),
  ng_               (false),
  auto_play_        (false),
  auto_record_      (false),
  notify_new_       (false),
  notify_change_    (false),
  notify_sound_rank_(-1)
{
  const Glib::ustring *font = NULL;
  const Glib::ustring *notify_sound = NULL;
  int rank = 0;
  for(FavoriteCollection::iterator iter = favorites->begin();
      iter != favorites->end(); ++iter) {
    if (!*(*iter)->enable()) continue;
    RefFavorite favorite = *iter;
    if (favorite->match(ch)) {
      matched_.push_back(*iter);

      if (*favorite->bgcolor()) {
        bgcolor_ = *favorite->bgcolor();
      }
      if (*favorite->fgcolor()) {
        fgcolor_ = *favorite->fgcolor();
      }
      if (!(*favorite->font()).empty()) {
        font = &*favorite->font();
      }
      if (*favorite->sort() != CHSORT_NONE) {
        sort_ = *favorite->sort();
      }

      if (!boost::logic::indeterminate(*favorite->ng())) {
        ng_ = *favorite->ng();
      }
      if (!boost::logic::indeterminate(*favorite->auto_play())) {
        auto_play_ = *favorite->auto_play();
      }
      if (!boost::logic::indeterminate(*favorite->auto_record())) {
        auto_record_ = *favorite->auto_record();
      }
      if (!boost::logic::indeterminate(*favorite->notify_new())) {
        notify_new_ = *favorite->notify_new();
      }
      if (!boost::logic::indeterminate(*favorite->notify_change())) {
        notify_change_ = *favorite->notify_change();
      }

      if (!(*favorite->notify_sound()).empty()) {
        notify_sound = &*favorite->notify_sound();
        notify_sound_volume_ = *favorite->notify_sound_volume();
        notify_sound_rank_ = rank;
      }
    }
    ++rank;
  }
  if (font) {
    font_ = *font;
  }
  if (notify_sound) {
    notify_sound_ = *notify_sound;
  }
}

}/*gpyp*/
